const PENDDING = 'pendding'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
// let FULFILLED_CALLBACK_LIST = []
// let REJECTED_CALLBACK_LIST = []
// let _status = PENDDING

class MPromise {
    FULFILLED_CALLBACK_LIST = []
    REJECTED_CALLBACK_LIST = []
    _status = PENDDING

    /**
     * 
     * @param {Function} func 
     */
    constructor(func) {
        this.status = PENDDING
        this.value = null
        this.reason = null
        try {
            func(this.resolve.bind(this), this.reject.bind(this))
        } catch (error) {
            this.reject(error)
        }
    }

    get status() {
        return this._status
    }

    set status(newStatus) {
        this._status = newStatus

        switch (newStatus) {
            case FULFILLED:
                {
                    this.FULFILLED_CALLBACK_LIST.forEach(cb => cb(this.value))
                    break
                }
            case REJECTED:
                {
                    this.REJECTED_CALLBACK_LIST.forEach(cb => cb(this.reason))
                    break
                }
        }
    }

    resolve(value) {
        if (this.status === PENDDING) {
            this.value = value
            this.status = FULFILLED
        }
    }

    reject(reason) {
        if (this.status === PENDDING) {
            this.reason = reason
            this.status = REJECTED
        }
    }

    then(onFulfilled, onRejected) {
        const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => value
        const realOnRejected = this.isFunction(onRejected) ? onRejected : reason => { throw reason }

        const promise2 = new MPromise((resolve, reject) => {
            const fulfilledMicrotask = () => {
                queueMicrotask(() => {
                    try {
                        const x = realOnFulfilled(this.value)
                        this.resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            }
            const rejectedMicrotask = () => {
                queueMicrotask(() => {
                    try {
                        const x = realOnRejected(this.reason)
                        this.resolvePromise(promise2, x, resolve, reject)
                    } catch (error) {
                        reject(error)
                    }
                })
            }

            switch (this.status) {
                case FULFILLED:
                    {
                        console.log('33333')
                        fulfilledMicrotask()
                        break
                    }
                case REJECTED:
                    {
                        rejectedMicrotask()
                        break
                    }
                case PENDDING:
                    {
                        this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask)
                        this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask)
                        break
                    }
            }
        })
    }
    resolvePromise(promise2, x, resolve, reject) {
        if (promise2 === x) {
            return reject(new TypeError('The promise and the return value are the same'))
        }
        if (x instanceof MPromise) {
            queueMicrotask(() => {
                x.then((y) => {
                    this.resolvePromise(promise2, y, resolve, reject)
                }, reject)
            })
        } else if (typeof x === 'object' || this.isFunction(x)) {
            if (x === null) {
                return resolve(x)
            }
            let then = null
            try {
                then = x.then
            } catch (error) {
                return reject(error)
            }

            if (this.isFunction(then)) {
                let called = false
                try {
                    then.call(
                        x,
                        (y) => {
                            if (called) {
                                return
                            }
                            called = true
                            this.resolvePromise(promise2, y, resolve, reject)
                        },
                        (r) => {
                            if (called) {
                                return
                            }
                            called = true
                            reject(r)
                        }
                    )
                } catch (error) {
                    if (called) {
                        return
                    }
                    reject(error)
                }
            } else {
                resolve(x)
            }
        } else {
            resolve(x)
        }
    }
    catch(onRejected) {
        return this.then(null, onRejected)
    }
    
    isFunction(fn) {
        return typeof fn === 'function'
    }

    static resolve(value) {
        if (value instanceof MPromise) {
            return value
        }
        return new MPromise((resolve, reject) => {
            resolve(value)
        })
    }
    static reject(reason) {
        return new MPromise((resolve, reject) => {
            reject(reason)
        })
    }

    static race(promiseList) {
        return new MPromise((resolve, reject) => {
            const leng = promiseList.length
            if (leng == 0) {
                return resolve()
            } else {
                for (let i = 0; i < leng; i++) {
                    MPromise.resolve(promiseList[i]).then(
                        val => resolve(val),
                        reason => reject(reason)
                    )
                }
            }

        })
    }
}

const promise = new MPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('111133333333333')
    }, 3000)
}).then(res => {
    console.log(res)
})

// const obj = {
//   then: 'then---value'
// }

// Object.defineProperty(obj, 'then', {
//   get () {
//     throw new Error('获取then值报错')
//   }
// })

// try {
//   const x =  obj.then
// } catch (error) {
//   console.log('cuocuocuo ')
// }